<!DOCTYPE html>
<html>
  <head>
    <title>Listing 13.13</title>
    <meta charset="utf-8">
    <script type="text/javascript" src="../scripts/assert.js"></script>
    <link href="../styles/assert.css" rel="stylesheet" type="text/css">
  </head>
  <body>
    <script type="text/javascript">

      (function(){

        this.addChange = function (elem, fn) {                          //#A

          addEvent(elem, "change", fn);                                 //#B
          if (isEventSupported("change")) return;                       //#B

          if (getData(elem).events.change.length === 1) {               //#C Piggyback on first binding
            addEvent(elem, "focusout", triggerChangeIfValueChanged);
            addEvent(elem, "click", triggerChangeOnClick);
            addEvent(elem, "keydown", triggerChangeOnKeyDown);
            addEvent(elem, "beforceactivate", triggerChangeOnBefore);
          }
        };

        this.removeChange = function (elem, fn) {                       //#D

          removeEvent(elem, "change", fn);                              //#E
          if (isEventSupported("change")) return;                       //#E

          var data = getData(elem);
          if (!data || !data.events || !data.events.submit) {           //#F Remove piggybacks on last unbind
            addEvent(elem, "focusout", triggerChangeIfValueChanged);
            addEvent(elem, "click", triggerChangeOnClick);
            addEvent(elem, "keydown", triggerChangeOnKeyDown);
            addEvent(elem, "beforceactivate", triggerChangeOnBefore);
          }
        };

        function triggerChangeOnClick(e) {                               //#G
          var type = e.target.type;
          if (type === "radio" || type === "checkbox" ||
              e.target.nodeName.toLowerCase() === "select") {
            return triggerChangeIfValueChanged.call(this, e);
          }
        }

        function triggerChangeOnKeyDown(e) {                             //#H
          var type = e.target.type,
              key = e.keyCode;
          if (key === 13 && e.target.nodeName.toLowerCase() !== "textarea" ||
              key === 32 && (type === "checkbox" || type === "radio") ||
              type === "select-multiple") {
            return triggerChangeIfValueChanged.call(this, e);
          }
        }

        function triggerChangeOnBefore(e) {                               //#I Stores target value for later checking
          getData(e.target)._change_data = getVal(e.target);
        }

        function getVal(elem) {                                           //#J Utility to get element value
          var type = elem.type,
              val = elem.value;
          if (type === "radio" || type === "checkbox") {
            val = elem.checked;
          } else if (type === "select-multiple") {
            val = "";
            if (elem.selectedIndex > -1) {
              for (var i = 0; i < elem.options.length; i++) {
                val += "-" + elem.options[i].selected;
              }
            }
          } else if (elem.nodeName.toLowerCase() === "select") {
            val = elem.selectedIndex;
          }
          return val;
        }

        function triggerChangeIfValueChanged(e) {                            //#K Checks to see if a change in the value has occurred
          var elem = e.target, data, val;
          var formElems = /textarea|input|select/i;
          if (!formElems.test(elem.nodeName) || elem.readOnly) {
            return;
          }
          data = getData(elem)._change_data;
          val = getVal(elem);
          if (e.type !== "focusout" || elem.type !== "radio") {
            getData(elem)._change_data = val;
          }
          if (data === undefined || val === data) {
            return;
          }
          if (data != null || val) {
            return triggerEvent(elem, "change");
          }
        }

      })();

    </script>
  </body>
</html>

